package com.coocaa.x.service.litedaemon;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;

import com.coocaa.x.framework.app.CoocaaApplication;
import com.coocaa.x.framework.utils.Android;
import com.coocaa.x.framework.utils.Cache;
import com.coocaa.x.framework.utils.LogUtils;
import com.coocaa.x.service.litedaemon.daemons.tvpie.app.TVPieAppDaemon;
import com.coocaa.x.service.litedaemon.daemons.tvpie.player.TVPiePlayerDaemon;

import java.util.List;

import dalvik.system.DexClassLoader;

/**
 * Created by lu on 15-7-29.
 */
public class LiteDaemonServiceStub {

    private static final String MANIFEST_DAEMONS_ENTRY = "manifest_daemons";
    public static final String TAG = "LiteDaemon";

    private static final Class<?>[] DEFAULT_DAEMONS = {TVPiePlayerDaemon.class, TVPieAppDaemon.class};

    private Cache<String, String> exteranlDaemons = new Cache<String, String>();
    private Cache<String, LiteDaemon> daemons = new Cache<String, LiteDaemon>();

    private Context mContext = null;

    public LiteDaemonServiceStub(Context context) {
        mContext = context;
        initExternalDaemonMonitor();
        startDaemons();
    }

    public void destroy() {
        List<String> daemonNames = daemons.keys();
        for (String name : daemonNames)
            stopDaemon(name);
        exteranlDaemons.clear();
        daemons.clear();
        mContext.unregisterReceiver(receiver);
    }


    private BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, final Intent intent) {
            CoocaaApplication.post(new Runnable() {
                @Override
                public void run() {
                    String action = intent.getAction();
                    String pkgName = intent.getDataString().substring(8);
                    if (action.equals("android.intent.action.PACKAGE_ADDED")) {
                        try {
                            LogUtils.i(TAG, "PACKAGE_ADDED now try to start ExternalDaemon:"
                                    + pkgName);
                            ApplicationInfo appinfo = mContext.getPackageManager()
                                    .getApplicationInfo(pkgName, PackageManager.GET_META_DATA);
                            startExternalDaemon(appinfo);
                        } catch (PackageManager.NameNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                    if (action.equals("android.intent.action.PACKAGE_REMOVED")) {
                        if (exteranlDaemons.containsKey(pkgName)) {
                            LogUtils.i(TAG, "PACKAGE_REMOVED now try to stop ExternalDaemon:"
                                    + pkgName);
                            LiteDaemon daemon = daemons.get(exteranlDaemons.get(pkgName));
                            exteranlDaemons.remove(pkgName);
                            if (daemon != null)
                                stopDaemon(daemon.getInfo().id);
                        }
                    }
                }
            });
        }
    };

    private void initExternalDaemonMonitor() {
        IntentFilter filter = new IntentFilter();
        filter.addAction("android.intent.action.PACKAGE_ADDED");
        filter.addAction("android.intent.action.PACKAGE_REMOVED");
        filter.addDataScheme("package");
        mContext.registerReceiver(receiver, filter);
    }

    private void startDaemons() {
        for (Class<?> clazz : DEFAULT_DAEMONS) {
            try {
                LiteDaemon daemon = (LiteDaemon) clazz.newInstance();
                stopDaemon(daemon.getInfo().id);
                startDaemon(daemon, mContext);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        startExternalDaemons();
        startManifestDaemons();
    }

    private void startManifestDaemons() {
        String daemons = (String) Android.getMetaData(mContext, mContext.getPackageName(),
                MANIFEST_DAEMONS_ENTRY);
        if (daemons != null && !daemons.equals("")) {
            String[] daemons_cls = daemons.split("\\|");
            for (String daemon_cls : daemons_cls) {
                try {
                    Class<?> clazz = Class.forName(daemon_cls.trim());
                    LiteDaemon daemon = (LiteDaemon) clazz.newInstance();
                    stopDaemon(daemon.getInfo().id);
                    startDaemon(daemon, mContext);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void startExternalDaemons() {
        List<ApplicationInfo> list = mContext.getPackageManager().getInstalledApplications(
                PackageManager.GET_META_DATA);
        for (ApplicationInfo info : list)
            startExternalDaemon(info);
    }

    private synchronized void startExternalDaemon(ApplicationInfo info) {
        String entry = (String) Android.getMetaData(mContext, info.packageName,
                LiteDaemon.DAEMON_ENTRY_META_DATA_NAME);
        if (entry != null && !entry.equals("")) {
            try {
                LogUtils.d(TAG, "start load external daemon packageName:" + info.packageName + " entry:"
                        + entry);
                Context mExContext = mContext.createPackageContext(info.packageName,
                        Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE);
                DexClassLoader classLoader = new DexClassLoader(info.sourceDir, mContext.getDir(
                        "dex", 0).getAbsolutePath(), info.nativeLibraryDir,
                        mContext.getClassLoader());
                Class<?> clazz = classLoader.loadClass(entry);
                LiteDaemon daemon = (LiteDaemon) clazz.newInstance();

                if (exteranlDaemons.containsKey(info.packageName)) {
                    String name = exteranlDaemons.get(info.packageName);
                    stopDaemon(name);
                }
                exteranlDaemons.add(info.packageName, daemon.getInfo().id);

                stopDaemon(daemon.getInfo().id);
                startDaemon(daemon, mExContext);
            } catch (Exception e) {
                e.printStackTrace();
                LogUtils.d(TAG, " external daemon packageName:" + info.packageName + " entry:" + entry
                        + " load failed!!:" + e.getLocalizedMessage());
            }
        }
    }

    private void stopDaemon(String name) {
        if (daemons.containsKey(name)) {
            LiteDaemon _daemon = daemons.get(name);
            _daemon.stop();
            daemons.remove(name);
            _daemon = null;
        }
    }

    private void startDaemon(LiteDaemon daemon, Context context) {
        daemons.add(daemon.getInfo().id, daemon);
        daemon.setContext(context);
        daemon.start();
    }
}
